package com.hk.core.authentication.security.reactive;

import com.hk.core.authentication.api.AuthenticationException;
import com.hk.core.authentication.api.UserPrincipal;
import com.hk.core.authentication.api.reactive.ReactiveSecurityContext;
import lombok.Setter;
import org.springframework.security.authentication.AnonymousAuthenticationToken;
import org.springframework.security.core.context.ReactiveSecurityContextHolder;
import org.springframework.security.core.context.SecurityContext;
import reactor.core.publisher.Mono;

import java.util.Objects;
import java.util.function.Function;

/**
 * @author Kevin
 * @date 2022-06-24 20:04
 */
@Setter
public class ReactiveSpringSecurityContext implements ReactiveSecurityContext {

    /**
     * 获取用户最新的信息：
     * 定义这个的目的: 当用户修改了用户上下信息时，如果用户已登陆，用户的信息存放在session or 当前上下文中，
     * 这个用户信息并不是修改后的用户最新信息，如果想要获取用户最新的信息，可以通过此方法来获取
     */
    private Function<UserPrincipal, Mono<? extends UserPrincipal>> function;

    @Override
    public <T extends UserPrincipal> Mono<T> getPrincipalByType(Class<T> type) {
        return ReactiveSecurityContextHolder.getContext()
                .map(SecurityContext::getAuthentication)
                .filter(authentication -> Objects.nonNull(authentication)
                        && !(authentication instanceof AnonymousAuthenticationToken)
                        && authentication.isAuthenticated())
                .switchIfEmpty(Mono.error(new AuthenticationException("未认证的用户")))
                .flatMap(authentication -> {
                    var principal = (UserPrincipal) authentication.getPrincipal();
                    if (Objects.nonNull(function)) {
                        return function.apply(principal)
                                .map(type::cast);
                    }
                    return Mono.just(type.cast(principal));
                });
    }

    @Override
    public Mono<Boolean> isAuthenticated() {
        return ReactiveSecurityContextHolder.getContext()
                .map(SecurityContext::getAuthentication)
                .flatMap(authentication -> Mono.just(Objects.nonNull(authentication)
                        && !(authentication instanceof AnonymousAuthenticationToken)
                        && authentication.isAuthenticated()))
                .defaultIfEmpty(Boolean.FALSE);
    }
}
